Mestre Resource Timing API-et for å diagnostisere og optimalisere frontend-ytelse. Lær hvordan du måler lastetiden for hver ressurs, fra DNS-oppslag til nedlasting av innhold.
Frigjør frontend-ytelse: Et dypdykk i Resource Timing API-et
I webutviklingens verden er hastighet ikke bare en funksjon; det er et fundamentalt krav for en positiv brukeropplevelse. En nettside som laster sakte kan føre til høyere fluktfrekvens, lavere brukerengasjement, og til syvende og sist, en negativ innvirkning på forretningsmål. Mens verktøy som Lighthouse og WebPageTest gir uvurderlig diagnostikk på høyt nivå, representerer de ofte en enkelt, syntetisk test. For å virkelig forstå og optimalisere ytelsen for et globalt publikum, må vi måle opplevelsen til ekte brukere, på deres enheter, på deres nettverk. Det er her Real User Monitoring (RUM) kommer inn, og et av de kraftigste verktøyene er Resource Timing API-et.
Denne omfattende guiden vil ta deg med på et dypdykk i Resource Timing API-et. Vi vil utforske hva det er, hvordan du bruker det, og hvordan du kan omgjøre dets detaljerte data til handlingsrettet innsikt som dramatisk kan forbedre applikasjonens lastningsytelse. Enten du er en erfaren frontend-ingeniør eller nettopp har startet din reise med ytelsesoptimalisering, vil denne artikkelen utstyre deg med kunnskapen til å dissekere og forstå nettverksytelsen til hver eneste ressurs på siden din.
Hva er Resource Timing API-et?
Resource Timing API-et er et nettleserbasert JavaScript-API som gir detaljerte nettverkstidsdata for hver ressurs en nettside laster ned. Tenk på det som en mikroskopisk linse for sidens nettverksaktivitet. For hvert bilde, skript, stilark, skrifttype og API-kall (via `fetch` eller `XMLHttpRequest`), fanger dette API-et et høyoppløselig tidsstempel for hvert trinn i nettverksforespørselen.
Det er en del av en større pakke med Ytelses-API-er, som jobber sammen for å gi et helhetlig bilde av applikasjonens ytelse. Mens Navigation Timing API-et fokuserer på livssyklusen til hoveddokumentet, zoomer Resource Timing API-et inn på alle de avhengige ressursene som hoveddokumentet ber om.
Hvorfor er det så viktig?
- Granularitet: Det går utover en enkelt "sidelastningstid"-metrikk. Du kan se nøyaktig hvor lang tid DNS-oppslaget, TCP-tilkoblingen og nedlastingen av innhold tok for et spesifikt tredjepartsskript eller et kritisk heltebilde.
- Ekte brukerdata: I motsetning til laboratoriebaserte verktøy, kjører dette API-et i brukernes nettlesere. Dette lar deg samle inn ytelsesdata fra et mangfold av nettverksforhold, enheter og geografiske steder, noe som gir deg et sant bilde av din globale brukeropplevelse.
- Handlingsrettet innsikt: Ved å analysere disse dataene kan du identifisere spesifikke flaskehalser. Er et tredjeparts analyseskript tregt å koble til? Underpresterer din CDN i en bestemt region? Er bildene dine for store? Resource Timing API-et gir bevisene som trengs for å svare på disse spørsmålene med sikkerhet.
Anatomien til en ressursinnlasting: En analyse av tidslinjen
Kjernen i Resource Timing API-et er `PerformanceResourceTiming`-objektet. For hver ressurs som lastes, oppretter nettleseren et av disse objektene, som inneholder et vell av tids- og størrelsesinformasjon. For å forstå disse objektene, er det nyttig å visualisere lastingsprosessen som et fossefallsdiagram, der hvert trinn følger det forrige.
La oss bryte ned de viktigste egenskapene til et `PerformanceResourceTiming`-objekt. Alle tidsverdier er høyoppløselige tidsstempler målt i millisekunder fra starten av sidenavigasjonen (`performance.timeOrigin`).
startTime -> fetchStart -> domainLookupStart -> domainLookupEnd -> connectStart -> connectEnd -> requestStart -> responseStart -> responseEnd
Sentrale tidsegenskaper
name: URL-en til ressursen. Dette er din primære identifikator.entryType: En streng som indikerer typen ytelsesoppføring. For våre formål vil dette alltid være "resource".initiatorType: Dette er utrolig nyttig for feilsøking. Det forteller deg hvordan ressursen ble forespurt. Vanlige verdier inkluderer 'img', 'link' (for CSS), 'script', 'css' (for ressurser lastet fra CSS som `@import`), 'fetch' og 'xmlhttprequest'.duration: Den totale tiden ressursen tok, beregnet somresponseEnd - startTime. Dette er den overordnede metrikken for en enkelt ressurs.startTime: Tidsstempelet umiddelbart før ressurshentingen begynner.fetchStart: Tidsstempelet rett før nettleseren begynner å hente ressursen. Den kan sjekke hurtiglagre (HTTP-cache, Service Worker-cache) før den går videre til nettverket. Hvis ressursen serveres fra et hurtiglager, vil mange av de påfølgende tidsverdiene være null.domainLookupStart&domainLookupEnd: Disse markerer starten og slutten på DNS-oppslaget (Domain Name System). Varigheten (domainLookupEnd - domainLookupStart) er tiden det tok å løse domenenavnet til en IP-adresse. En høy verdi her kan indikere en treg DNS-leverandør.connectStart&connectEnd: Disse markerer starten og slutten på å etablere en tilkobling til serveren. For HTTP er dette TCP treveis håndtrykk. Varigheten (connectEnd - connectStart) er din TCP-tilkoblingstid.secureConnectionStart: Hvis ressursen lastes over HTTPS, markerer dette tidsstempelet begynnelsen på SSL/TLS-håndtrykket. Varigheten (connectEnd - secureConnectionStart) forteller deg hvor lang tid krypteringsforhandlingen tok. Treg TLS-håndtrykk kan være et tegn på feilkonfigurering av serveren eller nettverkslatens.requestStart: Tidsstempelet rett før nettleseren sender den faktiske HTTP-forespørselen for ressursen til serveren. Tiden mellomconnectEndogrequestStartkalles ofte "request queuing"-tid, der nettleseren venter på en tilgjengelig tilkobling.responseStart: Tidsstempelet da nettleseren mottar den aller første byten av responsen fra serveren. Varigheten (responseStart - requestStart) er den berømte Time to First Byte (TTFB). En høy TTFB er nesten alltid en indikator på en treg backend-prosess eller latens på serversiden.responseEnd: Tidsstempelet da den siste byten av ressursen er mottatt, og forespørselen er vellykket lukket. Varigheten (responseEnd - responseStart) representerer nedlastingstiden for innholdet.
Egenskaper for ressursstørrelse
Å forstå ressursstørrelse er like viktig som å forstå timing. API-et gir tre sentrale metrikker:
transferSize: Størrelsen i bytes på ressursen som overføres over nettverket, inkludert headere og den komprimerte responskroppen. Hvis ressursen ble servert fra et hurtiglager, vil dette ofte være 0. Dette er tallet som direkte påvirker brukerens dataplan og nettverkstid.encodedBodySize: Størrelsen i bytes på nyttelastkroppen *etter* komprimering (f.eks. Gzip eller Brotli), men *før* dekomprimering. Dette hjelper deg med å forstå størrelsen på selve nyttelasten, atskilt fra headerne.decodedBodySize: Størrelsen i bytes på nyttelastkroppen i sin ukomprimerte, opprinnelige form. Å sammenligne dette medencodedBodySizeavslører effektiviteten av komprimeringsstrategien din. Hvis disse to tallene er veldig like for en tekstbasert ressurs (som JS, CSS eller HTML), fungerer sannsynligvis ikke komprimeringen din som den skal.
Server Timing
En av de kraftigste integrasjonene med Resource Timing API-et er `serverTiming`-egenskapen. Din backend kan sende ytelsesmetrikker i en spesiell HTTP-header (`Server-Timing`), og disse metrikkene vil vises i `serverTiming`-arrayet på det tilsvarende `PerformanceResourceTiming`-objektet. Dette bygger bro mellom ytelsesovervåking på frontend og backend, og lar deg se database-spørringstider eller API-behandlingsforsinkelser direkte i dine frontend-data.
For eksempel kan en backend sende denne headeren:
Server-Timing: db;dur=53, api;dur=47.2, cache;desc="HIT"
Disse dataene vil være tilgjengelige i `serverTiming`-egenskapen, noe som lar deg korrelere en høy TTFB med en spesifikk treg prosess på backend.
Slik får du tilgang til Resource Timing-data i JavaScript
Nå som vi forstår dataene som er tilgjengelige, la oss se på de praktiske måtene å samle dem inn på ved hjelp av JavaScript. Det er to primære metoder.
Metode 1: `performance.getEntriesByType('resource')`
Dette er den enkleste måten å komme i gang på. Denne metoden returnerer en matrise av alle `PerformanceResourceTiming`-objekter for ressurser som allerede har lastet ferdig på siden på tidspunktet for kallet.
// Vent på at siden lastes for å sikre at de fleste ressursene fanges opp
window.addEventListener('load', () => {
const resources = performance.getEntriesByType('resource');
resources.forEach((resource) => {
console.log(`Ressurs lastet: ${resource.name}`);
console.log(` - Total tid: ${resource.duration.toFixed(2)}ms`);
console.log(` - Initiator: ${resource.initiatorType}`);
console.log(` - Overføringsstørrelse: ${resource.transferSize} bytes`);
});
});
Begrensning: Denne metoden er et øyeblikksbilde. Hvis du kaller den for tidlig, vil du gå glipp av ressurser som ikke har lastet ennå. Hvis applikasjonen din laster ressurser dynamisk lenge etter den første sidelastningen, må du polle denne metoden gjentatte ganger, noe som er ineffektivt.
Metode 2: `PerformanceObserver` (Den anbefalte metoden)
`PerformanceObserver` er en mer moderne, robust og ytelseseffektiv måte å samle inn ytelsesoppføringer på. I stedet for at du poller etter data, skyver nettleseren nye oppføringer til din observatør-callback etter hvert som de blir tilgjengelige.
Her er hvorfor det er bedre:
- Asynkron: Den blokkerer ikke hovedtråden.
- Omfattende: Den kan fange opp oppføringer helt fra begynnelsen av sidelastningen, og unngår race conditions der et skript kjører etter at en ressurs allerede er lastet.
- Effektiv: Den unngår behovet for polling med `setTimeout` eller `setInterval`.
Her er en standard implementering:
try {
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
// Behandle hver ressursoppføring etter hvert som den kommer inn
if (entry.entryType === 'resource') {
console.log(`Ressurs observert: ${entry.name}`);
console.log(` - Time to First Byte (TTFB): ${(entry.responseStart - entry.requestStart).toFixed(2)}ms`);
}
});
});
// Start observering for 'resource'-oppføringer.
// 'buffered'-flagget sikrer at vi får oppføringer som ble lastet før observatøren ble opprettet.
observer.observe({ type: 'resource', buffered: true });
// Du kan stoppe observeringen senere om nødvendig
// observer.disconnect();
} catch (e) {
console.error('PerformanceObserver støttes ikke i denne nettleseren.');
}
Alternativet buffered: true er kritisk. Det forteller observatøren å umiddelbart sende alle `resource`-oppføringer som allerede er i nettleserens ytelsesbuffer, og sikrer at du får en komplett liste fra starten.
Håndtering av ytelsesbufferen
Nettlesere har en standardgrense for hvor mange ressurs-timing-oppføringer de lagrer (vanligvis 150). På veldig komplekse sider kan denne bufferen bli full. Når det skjer, utløser nettleseren en `resourcetimingbufferfull`-hendelse, og ingen nye oppføringer blir lagt til.
Du kan håndtere dette ved å:
- Øke bufferstørrelsen: Bruk `performance.setResourceTimingBufferSize(limit)` for å sette en høyere grense, for eksempel 300.
- Tømme bufferen: Bruk `performance.clearResourceTimings()` etter at du har behandlet oppføringene for å gi plass til nye.
performance.addEventListener('resourcetimingbufferfull', () => {
console.warn('Resource Timing-bufferen er full. Tømmer...');
// Behandle eksisterende oppføringer fra observatøren din først
// Tøm deretter bufferen
performance.clearResourceTimings();
// Du må kanskje justere bufferstørrelsen på nytt hvis dette skjer ofte
// performance.setResourceTimingBufferSize(500);
});
Praktiske bruksområder og handlingsrettet innsikt
Å samle inn data er bare det første steget. Den virkelige verdien ligger i å omgjøre disse dataene til handlingsrettede forbedringer. La oss utforske noen vanlige ytelsesproblemer og hvordan Resource Timing API-et hjelper deg med å løse dem.
Bruksområde 1: Identifisere trege tredjepartsskript
Problemet: Tredjepartsskript for analyse, reklame, kundestøtte-widgets og A/B-testing er beryktede ytelsestyver. De kan være trege å laste, blokkere gjengivelse, og til og med forårsake ustabilitet.
Løsningen: Bruk Resource Timing API-et til å isolere og måle virkningen av disse skriptene på dine ekte brukere.
const observer = new PerformanceObserver((list) => {
const thirdPartyScripts = list.getEntries().filter(entry =>
entry.initiatorType === 'script' &&
!entry.name.startsWith(window.location.origin)
);
thirdPartyScripts.forEach(script => {
if (script.duration > 200) { // Sett en terskel, f.eks. 200ms
console.warn(`Tregt tredjepartsskript oppdaget: ${script.name}`, {
duration: `${script.duration.toFixed(2)}ms`,
transferSize: `${script.transferSize} bytes`
});
// I et ekte RUM-verktøy ville du sendt disse dataene til din analyse-backend.
}
});
});
observer.observe({ type: 'resource', buffered: true });
Handlingsrettet innsikt:
- Høy varighet: Hvis et skript konsekvent har lang varighet, vurder om det virkelig er nødvendig. Kan funksjonaliteten erstattes med et mer ytelseseffektivt alternativ?
- Lastestrategi: Laster skriptet synkront? Bruk `async`- eller `defer`-attributtene på `<script>`-taggen for å forhindre at det blokkerer sidens gjengivelse.
- Selektiv hosting: Kan skriptet lastes betinget, kun på sider der det er absolutt nødvendig?
Bruksområde 2: Optimalisering av bildelevering
Problemet: Store, uoptimaliserte bilder er en av de vanligste årsakene til trege sidelastninger, spesielt på mobile enheter med begrenset båndbredde.
Løsningen: Filtrer ressursoppføringer etter `initiatorType: 'img'` og analyser størrelsen og lastetiden deres.
// ... inne i en PerformanceObserver-callback ...
list.getEntries()
.filter(entry => entry.initiatorType === 'img')
.forEach(image => {
const downloadTime = image.responseEnd - image.responseStart;
// Et stort bilde kan ha en høy nedlastingstid og en stor transferSize
if (downloadTime > 500 || image.transferSize > 100000) { // 500ms eller 100KB
console.log(`Potensielt problem med stort bilde: ${image.name}`, {
downloadTime: `${downloadTime.toFixed(2)}ms`,
transferSize: `${(image.transferSize / 1024).toFixed(2)} KB`
});
}
});
Handlingsrettet innsikt:
- Høy `transferSize` og `downloadTime`: Dette er et klart signal om at bildet er for stort. Optimaliser det ved å bruke moderne formater som WebP eller AVIF, komprimere det riktig, og endre størrelsen til de viste dimensjonene.
- Bruk `srcset`: Implementer responsive bilder ved å bruke `srcset`-attributtet for å servere forskjellige bildestørrelser basert på brukerens visningsport.
- Lazy Loading: For bilder under folden, bruk `loading="lazy"` for å utsette lastingen til brukeren ruller dem inn i synsfeltet.
Bruksområde 3: Diagnostisering av nettverksflaskehalser
Problemet: Noen ganger er ikke problemet selve ressursen, men nettverksstien til den. Treg DNS, latente tilkoblinger eller overbelastede servere kan alle redusere ytelsen.
Løsningen: Bryt ned `duration` i sine komponentfaser for å finne kilden til forsinkelsen.
function analyzeNetworkPhases(resource) {
const dnsTime = resource.domainLookupEnd - resource.domainLookupStart;
const tcpTime = resource.connectEnd - resource.connectStart;
const ttfb = resource.responseStart - resource.requestStart;
const downloadTime = resource.responseEnd - resource.responseStart;
console.log(`Analyse for ${resource.name}`);
if (dnsTime > 50) console.warn(` - Høy DNS-tid: ${dnsTime.toFixed(2)}ms`);
if (tcpTime > 100) console.warn(` - Høy TCP-tilkoblingstid: ${tcpTime.toFixed(2)}ms`);
if (ttfb > 300) console.warn(` - Høy TTFB (treg server): ${ttfb.toFixed(2)}ms`);
if (downloadTime > 500) console.warn(` - Treg nedlasting av innhold: ${downloadTime.toFixed(2)}ms`);
}
// ... kall analyzeNetworkPhases(entry) inne i din observatør ...
Handlingsrettet innsikt:
- Høy DNS-tid: Din DNS-leverandør kan være treg. Vurder å bytte til en raskere global leverandør. Du kan også bruke `` for å løse DNS for kritiske tredjepartsdomener på forhånd.
- Høy TCP-tid: Dette indikerer latens i etableringen av tilkoblingen. Et Content Delivery Network (CDN) kan redusere dette ved å servere ressurser fra et sted som er geografisk nærmere brukeren. Å bruke `` kan utføre både DNS-oppslag og TCP-håndtrykk tidlig.
- Høy TTFB: Dette peker på en treg backend. Samarbeid med backend-teamet ditt for å optimalisere databasespørringer, forbedre server-side caching, eller oppgradere servermaskinvare. `Server-Timing`-headeren er din beste venn her.
- Høy nedlastingstid: Dette er en funksjon av ressursstørrelse og nettverksbåndbredde. Optimaliser ressursen (komprimer, minimer) eller bruk en CDN for å forbedre gjennomstrømningen.
Begrensninger og hensyn
Selv om det er utrolig kraftig, har Resource Timing API-et noen viktige begrensninger man må være klar over.
Kryss-opprinnelse ressurser og `Timing-Allow-Origin`-headeren
Av sikkerhetsgrunner begrenser nettlesere timing-detaljene som er tilgjengelige for ressurser lastet fra en annen opprinnelse (domene, protokoll eller port) enn hovedsiden din. Som standard, for en kryss-opprinnelse ressurs, vil de fleste timing-egenskaper som `redirectStart`, `domainLookupStart`, `connectStart`, `requestStart`, `responseStart`, og størrelsesegenskaper som `transferSize` være null.
For å eksponere disse detaljene, må serveren som hoster ressursen inkludere `Timing-Allow-Origin` (TAO) HTTP-headeren. For eksempel:
Timing-Allow-Origin: * (Tillater enhver opprinnelse å se timing-detaljene)
Timing-Allow-Origin: https://www.your-website.com (Tillater kun din nettside)
Dette er avgjørende når du jobber med dine egne CDN-er eller API-er på forskjellige subdomener. Sørg for at de er konfigurert til å sende TAO-headeren slik at du kan få full ytelsessynlighet.
Nettleserstøtte
Resource Timing API-et, inkludert `PerformanceObserver`, er bredt støttet på tvers av alle moderne nettlesere (Chrome, Firefox, Safari, Edge). For eldre nettlesere kan det imidlertid hende at det ikke er tilgjengelig. Pakk alltid koden din inn i en `try...catch`-blokk eller sjekk for eksistensen av `window.PerformanceObserver` før du bruker det for å unngå feil på eldre klienter.
Konklusjon: Fra data til beslutninger
Resource Timing API-et er et essensielt instrument i den moderne webutviklerens verktøykasse. Det avmystifiserer nettverksfossefallet, og gir de rå, detaljerte dataene som trengs for å gå fra vage klager om at "siden er treg" til presise, datadrevne diagnoser som "vår tredjeparts chat-widget har en 400ms TTFB for brukere i Sørøst-Asia."
Ved å utnytte `PerformanceObserver` til å samle inn ekte brukerdata og analysere hele livssyklusen til hver ressurs, kan du:
- Stille tredjepartsleverandører til ansvar for deres ytelse.
- Validere effektiviteten av din CDN og caching-strategier over hele verden.
- Finne og fikse overdimensjonerte bilder og uoptimaliserte ressurser.
- Korrelere frontend-forsinkelser med backend-behandlingstider.
Reisen til et raskere nett er kontinuerlig. Start i dag. Åpne nettleserens utviklerkonsoll, kjør kodebitene fra denne artikkelen på din egen side, og begynn å utforske de rike ytelsesdataene som har ventet på deg hele tiden. Ved å måle det som betyr noe, kan du bygge raskere, mer robuste og mer behagelige opplevelser for alle brukerne dine, uansett hvor de er i verden.